package encryption

import (
	"awesomeProject1/pkg/e"
	"crypto"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"io"
	"strings"
)

func AesEncrypt(plainText string, aesKey string) (cipherText string, code int, errStr string) {
	textBytes := []byte(plainText)
	key := []byte(aesKey)

	if len(key) != 16 && len(key) != 24 && len(key) != 32 {
		code = e.AesErrKeyLengthError
		return
	}

	//1.指定使用的加密aes算法
	block, err := aes.NewCipher(key)
	if err != nil {
		code = e.AesErrorFromEncryption
		errStr = err.Error()
		return
	}

	//2.不需要填充,直接获取ctr分组模式的stream
	// 返回一个计数器模式的、底层采用block生成key流的Stream接口，初始向量iv的长度必须等于block的块尺寸。
	// The IV needs to be unique, but not secure. Therefore it's common to
	// include it at the beginning of the ciphertext.
	cipherTextBytes := make([]byte, aes.BlockSize+len(textBytes))
	iv := cipherTextBytes[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		code = e.AesErrorFromEncryption
		errStr = err.Error()
		return
	}

	stream := cipher.NewCTR(block, iv)

	//3.加密操作
	stream.XORKeyStream(cipherTextBytes[aes.BlockSize:], textBytes)

	//base64编码
	cipherText = base64.StdEncoding.EncodeToString(cipherTextBytes)

	return cipherText, e.Ok, ""
}

func AesDecrypt(cipherText string, aesKey string) (plainText string, code int, errStr string) {
	if len(strings.TrimSpace(cipherText)) < 1 {
		code = e.AesErrCipherTextIsEmpty
		return
	}

	//解码
	cipherTextBytes, err2 := base64.StdEncoding.DecodeString(cipherText)
	if err2 != nil {
		code = e.ErrorFromDecryption2
		return
	}

	key := []byte(aesKey)

	if len(key) != 16 && len(key) != 24 && len(key) != 32 {
		code = e.AesErrKeyLengthError
		return
	}

	//1.指定使用的解密aes算法
	block, err := aes.NewCipher(key)
	if err != nil {
		code = e.AesErrorFromDecryption
		errStr = err.Error()
		return
	}

	iv := cipherTextBytes[:aes.BlockSize]
	plainTextBytes := make([]byte, len(cipherTextBytes)-aes.BlockSize)
	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(plainTextBytes, cipherTextBytes[aes.BlockSize:])
	plainText = string(plainTextBytes)
	return plainText, e.Ok, ""
}

func RsaEncrypt(plainText string, rsaPublicKey string) (cipherText string, code int, errStr string) {

	if len(strings.TrimSpace(plainText)) < 1 {
		code = e.RsaErrOriginallyTextIsEmpty
		return
	}
	if len(plainText) > 100000 {
		code = e.RsaErrTextTooMach
		return
	}

	block, _ := pem.Decode([]byte(rsaPublicKey))
	//if block == nil || strings.Contains(block.Type, "PUBLIC KEY") {
	if block == nil {
		code = e.PubKeyReadErr
		return
	}
	publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		code = e.PubKeyReadErr
		return
	}

	//类型断言
	publicKey := publicKeyInterface.(*rsa.PublicKey)
	if publicKey.Size() < 128 {
		code = e.RsaErrKeyAtLeast1024Bit
		return
	}

	//对明文进行加密
	cipherTextBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, []byte(plainText), []byte("AesKey"))
	if err != nil {
		code = e.RsaErrorFromEncryption
		errStr = err.Error()
		return
	}
	cipherText = base64.StdEncoding.EncodeToString(cipherTextBytes)
	return cipherText, e.Ok, ""
}

func RsaDecrypt(cipherText string, rsaKey string) (plainText string, code int, errStr string) {
	if len(strings.TrimSpace(cipherText)) < 1 {
		code = e.RsaErrCipherTextIsEmpty
		return

	}
	if len(cipherText) > 100000 {
		code = e.RsaErrTextTooMach
		return
	}

	block, _ := pem.Decode([]byte(rsaKey))
	if block == nil || strings.Contains(block.Type, "RSA PRIVATE KEY") {
		code = e.RsaPrivateKeyReadErr
		return
	}

	privateKeyObj, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		code = e.PubKeyReadErr
		errStr = err.Error()
		return
	}
	if privateKeyObj.Size() < 128 {
		code = e.RsaErrKeyAtLeast1024Bit
		return
	}

	//对加密原文解码
	cipherTextBytes, err := base64.StdEncoding.DecodeString(cipherText)
	if err != nil {
		code = e.RsaErrorFromDecryption
		errStr = err.Error()
		return
	}
	//对密文进行解密
	plainTextBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKeyObj, cipherTextBytes, []byte("AesKey"))
	if err != nil {
		code = e.RsaErrorFromDecryption
		errStr = err.Error()
		return
	}

	plainText = string(plainTextBytes)
	return plainText, e.Ok, ""
}

func RsaSign(originText string, rsaPriKey string) (sign string, code int, errorStr string) {
	if len(rsaPriKey) < 128 {
		code = e.RsaErrKeyAtLeast1024Bit
		return
	}

	block, _ := pem.Decode([]byte(rsaPriKey))
	if block == nil || strings.Contains(block.Type, "RSA PRIVATE KEY") {
		code = e.RsaPrivateKeyReadErr
		return
	}

	privateKeyObj, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		code = e.RsaPrivateKeyParseErr
		return
	}
	if privateKeyObj.Size() < 128 {
		code = e.RsaErrKeyAtLeast1024Bit
		return
	}

	// crypto/rand.Reader is a good source of entropy for blinding the RSA
	// operation.
	rng := rand.Reader

	// Only small messages can be signed directly; thus the hash of a
	// message, rather than the message itself, is signed. This requires
	// that the hash function be collision resistant. SHA-256 is the
	// least-strong hash function that should be used for this at the time
	// of writing (2016).
	hashed := sha256.Sum256([]byte(originText))

	signature, err := rsa.SignPKCS1v15(rng, privateKeyObj, crypto.SHA256, hashed[:])
	if err != nil {
		code = e.RsaErrSignFailed
		errorStr = err.Error()
		return
	}

	sign = base64.StdEncoding.EncodeToString(signature)
	return sign, e.Ok, ""
}

func RsaVerifySign(originText string, rsaPublicKey string, signatureEncoded string) (
	verified bool, code int, errorStr string) {

	if len(rsaPublicKey) < 128 {
		code = e.RsaErrKeyAtLeast1024Bit
		return
	}

	block, _ := pem.Decode([]byte(rsaPublicKey))
	//if block == nil || block.Type != "PUBLIC KEY" {
	if block == nil {
		code = e.PubKeyReadErr
		return
	}
	pk, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		errorStr = err.Error()
		code = e.PubKeyReadErr
		return
	}

	hashed := sha256.Sum256([]byte(originText))

	//签名信息
	signature, err := base64.StdEncoding.DecodeString(signatureEncoded)
	if err != nil {
		errorStr = err.Error()
		code = e.RsaErrBaseDecodeFailed
		return
	}
	err = rsa.VerifyPKCS1v15(pk.(*rsa.PublicKey), crypto.SHA256, hashed[:], signature)
	if err != nil {
		errorStr = err.Error()
		code = e.RsaErrSignatureVerificationFailed
		return
	}

	verified = true
	return
}
